#include "pch.h"
#include "windows.ui.xaml.media.dxinterop.h"

#include "GameRenderer.h"
#include "ConstantBuffers.h"
#include "DeferredConstantBuffers.h"
#include "GameEngine.h"

#include "../DirectX/DirectXHelper.h"
#include "../Utilities/BasicLoader.h"

#include "../GameObjects/Sphere.h"
#include "../Meshes/SphereMesh.h"

#include "../GameObjects/Cube.h"
#include "../Meshes/CubeMesh.h"

#include "../GameObjects/Cylinder.h"
#include "../Meshes/CylinderMesh.h"

#include "../GameObjects/Face.h"
#include "../Meshes/FaceMesh.h"

#include "../GameObjects/WallTile.h"
#include "../Meshes/WallTileMesh.h"

#include "../Meshes/GlassShatterScreenMesh.h"

#include "../GameObjects/Paddle.h"
#include "../GameObjects/Puck.h"
#include "../GameObjects/WallLight.h"

#include "../GameObjects/LightBase.h"
#include "../GameObjects/PointLight.h"

#include "../GameObjects/MjpegCamera.h"

#include "../GameObjects/Material.h"
#include "../GameObjects/NormalMappedMaterial.h"
#include "../GameObjects/GameConstants.h"
#include <string>
#include <sstream>
#include <ios>
#include <iomanip>

using namespace D2D1;
using namespace DirectX;
using namespace Microsoft::WRL;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Core;
using namespace concurrency;

namespace FPSCounter
{
	static int FrameCount = 0;
	static float LastUpdateTime = 0.0f;
}

void GameRenderer::ApplyBloom(bool apply)
{
	m_applyBloom = apply;
	if ( apply ) {
		m_currentBloomThreshold = GameConstants::BloomValues::ThresholdLow;
	} else {
		m_currentBloomThreshold = GameConstants::BloomValues::ThresholdHigh;
	}
}

GameRenderer::GameRenderer()
{
	m_gameResourcesLoaded = false;
	m_mainRenderTarget = CreateRenderTarget(true);
	m_bloomRenderTargetOne = CreateRenderTarget();
	m_bloomRenderTargetTwo = CreateRenderTarget();
	m_lightPrePassPositionBufferRenderTarget = CreateRenderTarget(true, true, 1.0f, 1.0f, DXGI_FORMAT_R32G32B32A32_FLOAT);
	m_lightPrePassNormalBufferRenderTarget = CreateRenderTarget(false, true, 1.0f, 1.0f, DXGI_FORMAT_R32G32B32A32_FLOAT);
	m_lightPassLightBufferRenderTarget = CreateRenderTarget(true);

	m_glassShatterScreenIsRunning = false;
}

void GameRenderer::Initialize(Windows::UI::Core::CoreWindow^ window, Windows::UI::Xaml::Controls::SwapChainBackgroundPanel^ panel, float dpi)
{
	DirectXBase::Initialize(window, panel, dpi);
	m_loader = ref new BasicLoader(m_d3dDevice.Get());

	auto pointLight = ref new PointLight();
	pointLight->LightColor(1.2f, 0.0f, 0.0f);
	pointLight->LightRange(30.0f);
	pointLight->LightPosition(10.0f, 5.0f, 1000.0f);

	m_allLights.push_back(pointLight);
	m_pointLights.push_back(pointLight);

	pointLight = ref new PointLight();
	pointLight->LightColor(0.0f, 1.0f, 0.0f);
	pointLight->LightRange(30.0f);
	pointLight->LightPosition(10.0f, 5.0f, 1000.0f);

	m_allLights.push_back(pointLight);
	m_pointLights.push_back(pointLight);

	pointLight = ref new PointLight();
	pointLight->LightColor(0.0f, 2.0f, 1.0f);
	pointLight->LightRange(16.0f);
	pointLight->LightPosition(10.0f, 5.0f, 1000.0f);

	m_allLights.push_back(pointLight);
	m_pointLights.push_back(pointLight);

	pointLight = ref new PointLight();
	pointLight->LightColor(0.19f, 0.49f, 0.69f);
	pointLight->LightRange(50.0f);
	pointLight->LightPosition(GameConstants::MaxBound.x/2.0f, GameConstants::MaxBound.y/2.0f, GameConstants::MaxBound.z + 2.0f);

	m_allLights.push_back(pointLight);
	m_pointLights.push_back(pointLight);

	pointLight = ref new PointLight();
	pointLight->LightColor(0.19f, 0.49f, 0.69f);
	pointLight->LightRange(50.0f);
	pointLight->LightPosition(GameConstants::MaxBound.x/2.0f, GameConstants::MaxBound.y/2.0f, GameConstants::MinBound.z - 2.0f);

	m_allLights.push_back(pointLight);
	m_pointLights.push_back(pointLight);

	m_currentBloomThreshold = GameConstants::BloomValues::ThresholdHigh;
}

task<void> GameRenderer::CreateGameDeviceResourcesAsync(_In_ GameEngine^ game)
{
	m_game = game;

	D3D11_BUFFER_DESC bd;
	ZeroMemory(&bd, sizeof(bd));

	// CREATE THE CONSTANT BUFFERS

	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.ByteWidth = 0;

	// CREATE THE CONSTANT BUFFERS FOR THE THREE PHASES OF BLOOM IMPLEMENTATION.
	bd.ByteWidth = (sizeof(BloomExtractConstantBufferChangesEveryFrame) + 15) / 16 * 16; // Buffers must have a byte size that is a multiple of 16, hence the + 15) / 16 * 16 code.
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_bloomExtractConstantBufferChangesEveryFrame)
		);

	bd.ByteWidth = (sizeof(BloomBlurConstantBufferChangesEveryFrame) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_bloomBlurHorizontalConstantBufferChangesEveryFrame)
		);
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_bloomBlurVerticalConstantBufferChangesEveryFrame)
		);

	bd.ByteWidth = (sizeof(BloomCombineConstantBufferChangesEveryFrame) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_bloomCombineConstantBufferChangesEveryFrame)
		);

	// CREATE THE CONSTANT BUFFERS FOR THE THREE PASSES OF LIGHT PRE-PASS DEFERRED RENDERING.
	bd.ByteWidth = (sizeof(LightPrePassOnWindowSizeChange) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_lightPrePassOnWindowSizeChangeBuffer)
		);

	bd.ByteWidth = (sizeof(LightPrePassPerFrame) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_lightPrePassPerFrameBuffer)
		);

	bd.ByteWidth = (sizeof(LightPrePassPerMesh) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_lightPrePassPerMeshBuffer)
		);

	bd.ByteWidth = (sizeof(LightPassOnWindowSizeChange) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_lightPassOnWindowSizeChangeBuffer)
		);

	bd.ByteWidth = (sizeof(LightPassLightParameters) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_lightPassLightParametersBuffer)
		);

	bd.ByteWidth = (sizeof(LightPassViewParameters) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_lightPassViewParametersBuffer)
		);

	bd.ByteWidth = (sizeof(LightPassPointLightDetails) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_lightPassPointLightDetailsBuffer)
		);

	bd.ByteWidth = (sizeof(LightFinalPassOnWindowSizeChange) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_lightFinalPassOnWindowSizeChangeBuffer)
		);

	bd.ByteWidth = (sizeof(LightFinalPassPerFrame) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_lightFinalPassPerFrameBuffer)
		);

	bd.ByteWidth = (sizeof(LightFinalPassPerMesh) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_lightFinalPassPerMeshBuffer)
		);

	bd.ByteWidth = (sizeof(GlassShatterOnWindowSizeChange) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_glassShatterOnWindowSizeChangeBuffer)
		);

	bd.ByteWidth = (sizeof(GlassShatterPerFrame) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_glassShatterPerFrameBuffer)
		);

	bd.ByteWidth = (sizeof(GlassShatterPerMesh) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_glassShatterPerMeshBuffer)
		);

	// CREATE THE DRAW TEXTURED QUAD VERTEX SHADER'S CONSTANT BUFFER
	bd.ByteWidth = (sizeof(DrawTexturedQuadConstantBufferChangeOnResize) + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_drawTexturedQuadConstantBufferChangesEveryDraw)
		);

	// CREATE THE DRAW TEXTURED QUAD VERTEX BUFFER
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.ByteWidth = (sizeof(PTVertex) * 4 + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_drawTexturedQuadVertexBuffer)
		);

	bd.ByteWidth = (sizeof(PNTVertex) * 10 + 15) / 16 * 16;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, nullptr, &m_glassShatterVertexBuffer)
		);

	std::array<PNTVertex, 10> vertices;

	for (unsigned int i = 0; i < vertices.size(); ++i)
	{
		vertices[i] = PNTVertex();
		vertices[i].position = XMFLOAT3((i + 1.0f) * 1.5f, 0.0f, 0.0f);
		vertices[i].normal = XMFLOAT3(0.0f, 0.0f, 1.0f);
		vertices[i].textureCoordinate = XMFLOAT2(0.0f, 0.0f);
	}

	m_d3dContext->UpdateSubresource(m_glassShatterVertexBuffer.Get(), 0, nullptr, vertices.data(), 0, 0);

	// CREATE THE DRAW TEXTURED QUAD INDEX BUFFER
	bd.Usage = D3D11_USAGE_IMMUTABLE;
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.ByteWidth = (sizeof(short) * 6 + 15) / 16 * 16;

	// Create and populate the index buffer data for the textured quad. A textured quad is 
	// just two triangle that share two common vertices and thus form a quadrilateral. The 
	// arrangement of vertex indices here uses proper winding order so that neither of 
	// the triangles will be accidentally culled. The index buffer will never change so
	// we are specifying its data here and making it immutable for performance.
	std::array<short, 6> drawTexturedQuadIndices;
	drawTexturedQuadIndices[0] = 0;
	drawTexturedQuadIndices[1] = 1;
	drawTexturedQuadIndices[2] = 2;
	drawTexturedQuadIndices[3] = 1;
	drawTexturedQuadIndices[4] = 3;
	drawTexturedQuadIndices[5] = 2;

	// Whenever you want to specify initial data, you need a D3D11_SUBRESOURCE_DATA struct. 
	// The *Pitch members only matter for 2D and 3D resources. Since an index buffer is a 
	// 1D resource we can set them to zero.
	D3D11_SUBRESOURCE_DATA drawTexturedQuadSubresourceData;
	ZeroMemory(&drawTexturedQuadSubresourceData, sizeof(drawTexturedQuadSubresourceData));
	drawTexturedQuadSubresourceData.pSysMem = drawTexturedQuadIndices.data();
	drawTexturedQuadSubresourceData.SysMemPitch = 0;
	drawTexturedQuadSubresourceData.SysMemSlicePitch = 0;

	DX::ThrowIfFailed(
		m_d3dDevice->CreateBuffer(&bd, &drawTexturedQuadSubresourceData, &m_drawTexturedQuadIndexBuffer)
		);

	// CREATE THE SAMPLER STATE

	D3D11_SAMPLER_DESC sampDesc;
	ZeroMemory(&sampDesc, sizeof(sampDesc));
	sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
	sampDesc.MinLOD = 0;
	sampDesc.MaxLOD = FLT_MAX;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateSamplerState(&sampDesc, &m_samplerLinear)
		);

	sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
	sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
	sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
	sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateSamplerState(&sampDesc, &m_samplerPoint)
		);

	// Create the blend states.

	D3D11_BLEND_DESC1 blendDesc;
	ZeroMemory(&blendDesc, sizeof(blendDesc));
	blendDesc.AlphaToCoverageEnable = false;
	blendDesc.IndependentBlendEnable = false;

	for(int i = 0; i < 2; i++)
	{
		blendDesc.RenderTarget[i].BlendEnable = true;
		blendDesc.RenderTarget[i].LogicOpEnable = false;
		blendDesc.RenderTarget[i].SrcBlend = D3D11_BLEND_SRC_ALPHA;
		blendDesc.RenderTarget[i].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
		blendDesc.RenderTarget[i].BlendOp = D3D11_BLEND_OP_ADD;
		blendDesc.RenderTarget[i].SrcBlendAlpha = D3D11_BLEND_ONE;
		blendDesc.RenderTarget[i].DestBlendAlpha = D3D11_BLEND_ONE;
		blendDesc.RenderTarget[i].BlendOpAlpha = D3D11_BLEND_OP_ADD;
		blendDesc.RenderTarget[i].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	}

	DX::ThrowIfFailed(
		m_d3dDevice->CreateBlendState1(
		&blendDesc,
		&m_blendStateAlpha
		)
		);
	for(int i = 0; i < 2; i++)
	{
		blendDesc.RenderTarget[i].SrcBlend = D3D11_BLEND_ONE;
		blendDesc.RenderTarget[i].DestBlend = D3D11_BLEND_ZERO;
		blendDesc.RenderTarget[i].BlendEnable = false;
	}
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBlendState1(
		&blendDesc,
		&m_blendStateOpaque
		)
		);

	ZeroMemory(&blendDesc, sizeof(blendDesc));
	for(int i = 0; i < 2; i++)
	{
		blendDesc.RenderTarget[i].BlendEnable = true;
		blendDesc.RenderTarget[i].SrcBlend = D3D11_BLEND_SRC_COLOR;
		blendDesc.RenderTarget[i].DestBlend = D3D11_BLEND_ONE;
		blendDesc.RenderTarget[i].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
		blendDesc.RenderTarget[i].DestBlendAlpha = D3D11_BLEND_ONE;
		blendDesc.RenderTarget[i].BlendOp = D3D11_BLEND_OP_ADD;
		blendDesc.RenderTarget[i].BlendOpAlpha = D3D11_BLEND_OP_ADD;
		blendDesc.RenderTarget[i].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	}
	DX::ThrowIfFailed(
		m_d3dDevice->CreateBlendState1(
		&blendDesc,
		&m_blendStateAdditive
		)
		);

	///////////////
	D3D11_RASTERIZER_DESC rasterDesc;

	rasterDesc.FillMode = D3D11_FILL_SOLID;
	rasterDesc.CullMode = D3D11_CULL_BACK;
	rasterDesc.FrontCounterClockwise = FALSE;
	rasterDesc.DepthBias = D3D11_DEFAULT_DEPTH_BIAS;
	rasterDesc.DepthBiasClamp = D3D11_DEFAULT_DEPTH_BIAS_CLAMP;
	rasterDesc.SlopeScaledDepthBias = D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
	rasterDesc.DepthClipEnable = TRUE;
	rasterDesc.ScissorEnable = FALSE;
	rasterDesc.MultisampleEnable = FALSE;
	rasterDesc.AntialiasedLineEnable = FALSE;

	DX::ThrowIfFailed(
		m_d3dDevice->CreateRasterizerState(
		&rasterDesc,
		&m_rasterizerState
		)
		);

	rasterDesc.CullMode = D3D11_CULL_NONE;

	DX::ThrowIfFailed(
		m_d3dDevice->CreateRasterizerState(
		&rasterDesc,
		&m_rasterizerStateCullModeNone
		)
		);

	D3D11_DEPTH_STENCIL_DESC depthDesc;

	depthDesc.DepthEnable = TRUE;
	depthDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	depthDesc.DepthFunc = D3D11_COMPARISON_LESS;
	depthDesc.StencilEnable = TRUE;
	depthDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
	depthDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
	const D3D11_DEPTH_STENCILOP_DESC defaultStencilOp =
	{ D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_REPLACE, D3D11_COMPARISON_ALWAYS };
	depthDesc.FrontFace = defaultStencilOp;
	depthDesc.BackFace = defaultStencilOp;

	DX::ThrowIfFailed(
		m_d3dDevice->CreateDepthStencilState(
		&depthDesc,
		&m_depthStencilState
		)
		);

	depthDesc.DepthEnable = FALSE;
	depthDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
	depthDesc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
	DX::ThrowIfFailed(
		m_d3dDevice->CreateDepthStencilState(&depthDesc, &m_depthStencilStateNone)
		);

	///////////////

	// CREATE THE TASK VECTOR FOR LOADING SHADERS AND TEXTURES

	std::vector<task<void>> tasks;

	// LOAD SHADERS AS TASKS

	// Note: PNTVertexLayout is defined in ConstantBuffers.h and
	// represents the Position, Normal, and Texture Coordinates of each vertex.
	// Note: PTVertexLayout is defined in ConstantBuffer.h and
	// represents the Position and Texture Coordinates of each vertex.
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\DrawTexturedQuadVertexShader.cso", PTVertexLayout, ARRAYSIZE(PTVertexLayout), &m_drawTexturedQuadVertexShader, &m_drawTexturedQuadVertexLayout));
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\DrawTexturedQuadPixelShader.cso", &m_drawTexturedQuadPixelShader));
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\BloomExtractPixelShader.cso", &m_bloomExtractPixelShader));
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\BloomBlurPixelShader.cso", &m_bloomBlurPixelShader));
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\BloomCombinePixelShader.cso", &m_bloomCombinePixelShader));

	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\LightPrePassVertexShader.cso", PNTVertexLayout, ARRAYSIZE(PNTVertexLayout), &m_lightPrePassVertexShader, &m_vertexLayout));
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\LightPrePassNormalMapVertexShader.cso", PNTBTVertexLayout, ARRAYSIZE(PNTBTVertexLayout), &m_lightPrePassNormalMapVertexShader, &m_normalMapVertexLayout));
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\LightPrePassPixelShader.cso", &m_lightPrePassPixelShader));
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\LightPrePassNormalMapPixelShader.cso", &m_lightPrePassNormalMapPixelShader));

	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\LightPassVertexShader.cso", PTVertexLayout, ARRAYSIZE(PTVertexLayout), &m_lightPassVertexShader, &m_drawTexturedQuadVertexLayout));
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\LightPassPointLightPixelShader.cso", &m_lightPassPointLightPixelShader));

	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\LightFinalPassVertexShader.cso", PNTVertexLayout, ARRAYSIZE(PNTVertexLayout), &m_lightFinalPassVertexShader, &m_vertexLayout));
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\LightFinalPassPixelShader.cso", &m_lightFinalPassPixelShader));

	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\GlassShatterVertexShader.cso", PNTVertexLayout, ARRAYSIZE(PNTVertexLayout), &m_glassShatterVertexShader, &m_glassShatterVertexLayout));
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\GlassShatterGeometryShader.cso", &m_glassShatterGeometryShader));
	tasks.push_back(m_loader->LoadShaderAsync("Shaders\\GlassShatterPixelShader.cso", &m_glassShatterPixelShader));

	// LOAD TEXTURES AS TASKS
	m_C4FTexture = nullptr;
	m_TransparentTexture = nullptr;

	tasks.push_back(m_loader->LoadTextureAsync("textures\\texture.png", nullptr, &m_C4FTexture));
	tasks.push_back(m_loader->LoadTextureAsync("textures\\textureTransparent.dds", nullptr, &m_TransparentTexture));
	tasks.push_back(m_loader->LoadTextureAsync("textures\\textureLinen.dds", nullptr, &m_LinenTexture));
	tasks.push_back(m_loader->LoadTextureAsync("textures\\textureCracks.png", nullptr, &m_CracksTexture));
	tasks.push_back(m_loader->LoadTextureAsync("textures\\textureCracksNormal.dds", nullptr, &m_CracksNormalMapTexture));
	tasks.push_back(m_loader->LoadTextureAsync("textures\\textureCorner.png", nullptr, &m_CornerTexture));
	tasks.push_back(m_loader->LoadTextureAsync("textures\\textureCornerNormal.png", nullptr, &m_CornerNormalMapTexture));
	tasks.push_back(m_loader->LoadTextureAsync("textures\\textureDamage.png", nullptr, &m_DamageTexture));
	tasks.push_back(m_loader->LoadTextureAsync("textures\\texturePaddle.png", nullptr, &m_PaddleTexture));
	tasks.push_back(m_loader->LoadTextureAsync("textures\\textureGlass.png", nullptr, &m_GlassTexture));

	// RETURN WHEN ALL SHADER AND TEXTURE TASKS ARE COMPLETE
	return when_all(tasks.begin(), tasks.end());
}

void GameRenderer::CreateGameDeviceResourcesFinalize()
{
	// CREATE AND SET ALL LIGHT RESOURCES

	CreateGameDeviceResourcesLights();

	m_glassShatterScreenMesh = ref new GlassShatterScreenMesh(m_d3dDevice.Get(), m_glassShatterVertexLayout.Get());

	// CREATE CUBE RESOURCES
	MeshObject^ cubeMesh = ref new CubeMesh(m_d3dDevice.Get(), m_vertexLayout.Get());

	// CREATE CYLINDER RESOURCES
	MeshObject^ cylinderMesh = ref new CylinderMesh(m_d3dDevice.Get(), m_vertexLayout.Get(), 26);

	// CREATE SPHERE RESOURCES
	MeshObject^ sphereMesh = ref new SphereMesh(m_d3dDevice.Get(), m_vertexLayout.Get(), 26);

	// CREATE FACE RESOURCES
	MeshObject^ faceMesh = ref new FaceMesh(m_d3dDevice.Get(), m_vertexLayout.Get());

	// CREATE WALL TILE RESOURCES
	MeshObject^ tileMesh = ref new WallTileMesh(m_d3dDevice.Get(), m_normalMapVertexLayout.Get());

	// ASSOCIATE OBJECTS FROM THE GAME ENGINE WITH THE DEVICE RESOURCES (textures and meshes)
	auto objects = m_game->RenderObjects();

	for (auto object : m_game->TransparentRenderObjects())
	{
		objects.push_back(object);
	}

	for (auto object = objects.begin(); object != objects.end(); object++)
	{
		if ( Puck^ puck = dynamic_cast<Puck^>(*object))
		{
			Material^ m = ref new Material(
				XMFLOAT4(0.99f, 0.8f, 0.99f, 1.0f),
				XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
				XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
				1.0f,
				1.0f,
				1.0f,
				m_TransparentTexture.Get(),
				m_lightPrePassVertexShader.Get(),
				m_lightPrePassPixelShader.Get()
				);

			puck->Mesh(sphereMesh);
			puck->SetMaterial(m);
		} 
		else if ( WallLight^ wallLight = dynamic_cast<WallLight^>(*object))
		{
			Material^ m = ref new Material(
				XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
				GameConstants::Materials::WallLightColor,
				XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
				3.0f,
				1.0f,
				1.0f,
				m_TransparentTexture.Get(),
				m_lightPrePassVertexShader.Get(),
				m_lightPrePassPixelShader.Get()
				);

			wallLight->Mesh(cylinderMesh);
			wallLight->SetMaterial(m);
		}
		else if ( WallTile^ tile = dynamic_cast<WallTile^>(*object))
		{
			Material^ m = ref new NormalMappedMaterial(
				XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f),
				XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
				XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
				1.0f,
				0.2f,
				0.9f,
				m_CracksTexture.Get(),
				m_CracksNormalMapTexture.Get(),
				m_lightPrePassNormalMapVertexShader.Get(),
				m_lightPrePassNormalMapPixelShader.Get()
				);

			Material^ mCorner = ref new NormalMappedMaterial(
				XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f),
				XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
				XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
				15.0f,
				0.8f,
				0.0f,
				m_CornerTexture.Get(),
				m_CornerNormalMapTexture.Get(),
				m_lightPrePassNormalMapVertexShader.Get(),
				m_lightPrePassNormalMapPixelShader.Get()
				);

			tile->Mesh(tileMesh);
			if(tile->Corner())
				tile->SetMaterial(mCorner);
			else
				tile->SetMaterial(m);
		}
		else if ( Paddle^ paddle = dynamic_cast<Paddle^>(*object))
		{
			Material^ paddleMaterial = ref new Material(
				XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
				GameConstants::Materials::PaddleColor,
				XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
				0.5f,
				0.2f,
				2.0f,
				m_PaddleTexture.Get(),
				m_lightPrePassVertexShader.Get(),
				m_lightPrePassPixelShader.Get()
				);

			paddle->Mesh(sphereMesh);
			paddle->SetMaterial(paddleMaterial);
		}
		else if(MjpegCamera^ camera = dynamic_cast<MjpegCamera^>(*object))
		{
			Material^ mjpegMaterial = ref new Material(
				XMFLOAT4(.5f,.5f,.5f, 1.0f),
				XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
				XMFLOAT4(0.3f,0.3f,0.3f, 1.0f),
				5.0,
				0.0f,
				1.0f,
				m_C4FTexture.Get(),
				m_lightPrePassVertexShader.Get(),
				m_lightPrePassPixelShader.Get()
				);

			camera->Mesh(faceMesh);
			camera->SetMaterial(mjpegMaterial);
		}
	}

	// UDPATE CAMERA ASPECT RATIO FOR CURRENT DEVICE RENDERING SIZE
	m_game->GameCamera()->SetProjParams(
		XM_PI / 3,
		m_renderTargetSize.Width / m_renderTargetSize.Height,
		0.01f,
		100.0f
		);

	XMFLOAT4X4 projectionMatrix;

	XMStoreFloat4x4(
		&projectionMatrix,
		XMMatrixMultiply(
		XMMatrixTranspose(m_game->GameCamera()->Projection()), 
		XMMatrixTranspose(XMLoadFloat4x4(&m_orientationTransform3D))
		)
		);

	LightPrePassOnWindowSizeChange lightPrePassChangesOnResize;
	lightPrePassChangesOnResize.Projection = projectionMatrix;

	m_d3dContext->UpdateSubresource(
		m_lightPrePassOnWindowSizeChangeBuffer.Get(),
		0,
		nullptr,
		&lightPrePassChangesOnResize,
		0,
		0
		);

	LightFinalPassOnWindowSizeChange lightFinalPassOnWindowsizeChange = {};
	lightFinalPassOnWindowsizeChange.Projection = projectionMatrix;

	m_d3dContext->UpdateSubresource(
		m_lightFinalPassOnWindowSizeChangeBuffer.Get(),
		0,
		nullptr,
		&lightFinalPassOnWindowsizeChange,
		0,
		0
		);

	GlassShatterOnWindowSizeChange glassShatterOnWindowSizeChange = {};
	glassShatterOnWindowSizeChange.Projection = projectionMatrix;
	glassShatterOnWindowSizeChange.AmbientColor = XMFLOAT4(0.1f, 0.1f, 0.1f, 1.0f);
	glassShatterOnWindowSizeChange.DiffuseColor = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
	if (m_game->IsPlayerOne())
	{
		glassShatterOnWindowSizeChange.LightPositionAndParticleFadeOutTime =
			XMFLOAT4(GameConstants::EyeP1.x, GameConstants::EyeP1.y, GameConstants::EyeP1.z, GameConstants::Damage::DamageDuration);
	}
	else
	{
		glassShatterOnWindowSizeChange.LightPositionAndParticleFadeOutTime =
			XMFLOAT4(GameConstants::EyeP2.x, GameConstants::EyeP2.y, GameConstants::EyeP2.z, GameConstants::Damage::DamageDuration);
	}
	glassShatterOnWindowSizeChange.SpecularColorAndSpecularPower = XMFLOAT4(1.0f, 1.0f, 1.0f, GameConstants::Particles::SpecularPower);

	m_d3dContext->UpdateSubresource(
		m_glassShatterOnWindowSizeChangeBuffer.Get(), 0, nullptr, &glassShatterOnWindowSizeChange, 0, 0
		);

	PTVertex vertices[4];
	vertices[0].position = XMFLOAT3(0.0f, m_renderTargetSize.Height, 0.0f);
	vertices[0].textureCoordinate = XMFLOAT2(0.0f, 1.0f);
	vertices[1].position = XMFLOAT3(0.0f, 0.0f, 0.0f);
	vertices[1].textureCoordinate = XMFLOAT2(0.0f, 0.0f);
	vertices[2].position = XMFLOAT3(m_renderTargetSize.Width, m_renderTargetSize.Height, 0.0f);
	vertices[2].textureCoordinate = XMFLOAT2(1.0f, 1.0f);
	vertices[3].position = XMFLOAT3(m_renderTargetSize.Width, 0.0f, 0.0f);
	vertices[3].textureCoordinate = XMFLOAT2(1.0f, 0.0f);

	m_d3dContext->UpdateSubresource(m_drawTexturedQuadVertexBuffer.Get(), 0, nullptr, &vertices, 0, 0);

	m_gameResourcesLoaded = true;
}

void GameRenderer::CreateGameDeviceResourcesLights()
{
	Microsoft::WRL::ComPtr<ID3D11Buffer> lightParametersBuffer;
	Microsoft::WRL::ComPtr<ID3D11Buffer> lightDetailsBuffer;

	D3D11_BUFFER_DESC bd = {};
	bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bd.Usage = D3D11_USAGE_DEFAULT;

	for (auto item : m_pointLights)
	{
		bd.ByteWidth = (sizeof(LightPassLightParameters) + 15) / 16 * 16;
		DX::ThrowIfFailed(
			m_d3dDevice->CreateBuffer(&bd, nullptr, &lightParametersBuffer)
			);

		bd.ByteWidth = (sizeof(LightPassPointLightDetails) + 15) / 16 * 16;
		DX::ThrowIfFailed(
			m_d3dDevice->CreateBuffer(&bd, nullptr, &lightDetailsBuffer)
			);

		item->Initialize(m_lightPassPointLightPixelShader.Get(), lightParametersBuffer.Get(), lightDetailsBuffer.Get());
	}
}

void GameRenderer::CreateWindowSizeDependentResources()
{
	DirectXBase::CreateWindowSizeDependentResources();

	if (m_game != nullptr && m_game->GameCamera() != nullptr)
	{
		// UDPATE CAMERA ASPECT RATIO FOR CURRENT DEVICE RENDERING SIZE
		m_game->GameCamera()->SetProjParams(
			XM_PI / 3,
			m_renderTargetSize.Width / m_renderTargetSize.Height,
			0.01f,
			100.0f
			);

		XMFLOAT4X4 projectionMatrix;

		XMStoreFloat4x4(
			&projectionMatrix,
			XMMatrixMultiply(
			XMMatrixTranspose(m_game->GameCamera()->Projection()), 
			XMMatrixTranspose(XMLoadFloat4x4(&m_orientationTransform3D))
			)
			);

		LightPrePassOnWindowSizeChange lightPrePassChangesOnResize;
		lightPrePassChangesOnResize.Projection = projectionMatrix;

		m_d3dContext->UpdateSubresource(
			m_lightPrePassOnWindowSizeChangeBuffer.Get(),
			0,
			nullptr,
			&lightPrePassChangesOnResize,
			0,
			0
			);

		LightFinalPassOnWindowSizeChange lightFinalPassOnWindowsizeChange = {};
		lightFinalPassOnWindowsizeChange.Projection = projectionMatrix;

		m_d3dContext->UpdateSubresource(
			m_lightFinalPassOnWindowSizeChangeBuffer.Get(),
			0,
			nullptr,
			&lightFinalPassOnWindowsizeChange,
			0,
			0
			);


		GlassShatterOnWindowSizeChange glassShatterOnWindowSizeChange = {};
		glassShatterOnWindowSizeChange.Projection = projectionMatrix;
		glassShatterOnWindowSizeChange.AmbientColor = XMFLOAT4(0.1f, 0.1f, 0.1f, 1.0f);
		glassShatterOnWindowSizeChange.DiffuseColor = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
		if (m_game->IsPlayerOne())
		{
			glassShatterOnWindowSizeChange.LightPositionAndParticleFadeOutTime =
				XMFLOAT4(GameConstants::EyeP1.x, GameConstants::EyeP1.y, GameConstants::EyeP1.z, GameConstants::Damage::DamageDuration);
		}
		else
		{
			glassShatterOnWindowSizeChange.LightPositionAndParticleFadeOutTime =
				XMFLOAT4(GameConstants::EyeP2.x, GameConstants::EyeP2.y, GameConstants::EyeP2.z, GameConstants::Damage::DamageDuration);
		}
		glassShatterOnWindowSizeChange.SpecularColorAndSpecularPower = XMFLOAT4(1.0f, 1.0f, 1.0f, GameConstants::Particles::SpecularPower);

		m_d3dContext->UpdateSubresource(
			m_glassShatterOnWindowSizeChangeBuffer.Get(), 0, nullptr, &glassShatterOnWindowSizeChange, 0, 0
			);

		PTVertex vertices[4];
		vertices[0].position = XMFLOAT3(0.0f, m_renderTargetSize.Height, 0.0f);
		vertices[0].textureCoordinate = XMFLOAT2(0.0f, 1.0f);
		vertices[1].position = XMFLOAT3(0.0f, 0.0f, 0.0f);
		vertices[1].textureCoordinate = XMFLOAT2(0.0f, 0.0f);
		vertices[2].position = XMFLOAT3(m_renderTargetSize.Width, m_renderTargetSize.Height, 0.0f);
		vertices[2].textureCoordinate = XMFLOAT2(1.0f, 1.0f);
		vertices[3].position = XMFLOAT3(m_renderTargetSize.Width, 0.0f, 0.0f);
		vertices[3].textureCoordinate = XMFLOAT2(1.0f, 0.0f);

		m_d3dContext->UpdateSubresource(m_drawTexturedQuadVertexBuffer.Get(), 0, nullptr, &vertices, 0, 0);
	}
}

void GameRenderer::Render()
{
	if (m_game == nullptr || !m_gameResourcesLoaded)
	{
		return;
	}

	if (m_game != nullptr && m_game->GetGameTimer() != nullptr)
	{
		++FPSCounter::FrameCount;
		auto currentTime = m_game->GetGameTimer()->PlayingTime();
		if (currentTime >= FPSCounter::LastUpdateTime + 1.0f)
		{
			std::wstringstream fpsStr;
			fpsStr << L"FPS: " << std::setprecision(1) << std::fixed << FPSCounter::FrameCount / (currentTime - FPSCounter::LastUpdateTime);
			m_game->GetMainPage()->SetFPSText(ref new Platform::String(fpsStr.str().c_str()));
			FPSCounter::LastUpdateTime = currentTime;
			FPSCounter::FrameCount = 0;
		}
	}
	int renderingPasses = 1;

	if (m_stereoEnabled)
	{
		renderingPasses = 2;
	}

	const float ClearColor[4] = {0.0f, 0.0f, 0.0f, 1.0f};

	for (int i = 0; i < renderingPasses; i++)
	{
		if (m_game != nullptr && m_gameResourcesLoaded)
		{
			ID3D11RenderTargetView* targets[] = { *m_lightPrePassPositionBufferRenderTarget->GetRenderTargetView(), *m_lightPrePassNormalBufferRenderTarget->GetRenderTargetView() };
			m_d3dContext->OMSetRenderTargets(ARRAYSIZE(targets), targets, m_lightPrePassPositionBufferRenderTarget->GetDepthStencilView());
			m_d3dContext->ClearRenderTargetView(*m_lightPrePassPositionBufferRenderTarget->GetRenderTargetView(), ClearColor);
			m_d3dContext->ClearDepthStencilView(m_lightPrePassPositionBufferRenderTarget->GetDepthStencilView(), D3D11_CLEAR_DEPTH, 1.0f, 0);
			m_d3dContext->ClearRenderTargetView(*m_lightPrePassNormalBufferRenderTarget->GetRenderTargetView(), ClearColor);
			m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());

			if (m_stereoEnabled || true)
			{
				// When doing stereo, it is necessary to update the projection matrix once per rendering pass.
				XMFLOAT4X4 projectionMatrix;
				XMStoreFloat4x4(
					&projectionMatrix,
					XMMatrixMultiply(
					XMMatrixTranspose(
					i == 0 ?
					m_game->GameCamera()->LeftEyeProjection() :
					m_game->GameCamera()->RightEyeProjection()
					),
					XMMatrixTranspose(XMLoadFloat4x4(&m_orientationTransform3D))
					)
					);

				LightPrePassOnWindowSizeChange lightPrePassChangesOnResize;
				lightPrePassChangesOnResize.Projection = projectionMatrix;

				m_d3dContext->UpdateSubresource(
					m_lightPrePassOnWindowSizeChangeBuffer.Get(),
					0,
					nullptr,
					&lightPrePassChangesOnResize,
					0,
					0
					);

				LightFinalPassOnWindowSizeChange lightFinalPassOnWindowSizeChange = {};
				lightFinalPassOnWindowSizeChange.Projection = projectionMatrix;

				m_d3dContext->UpdateSubresource(
					m_lightFinalPassOnWindowSizeChangeBuffer.Get(),
					0,
					nullptr,
					&lightFinalPassOnWindowSizeChange,
					0,
					0
					);

				GlassShatterOnWindowSizeChange glassShatterOnWindowSizeChange = {};
				glassShatterOnWindowSizeChange.Projection = projectionMatrix;
				glassShatterOnWindowSizeChange.AmbientColor = XMFLOAT4(0.1f, 0.1f, 0.1f, 1.0f);
				glassShatterOnWindowSizeChange.DiffuseColor = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
				if (m_game->IsPlayerOne())
				{
					glassShatterOnWindowSizeChange.LightPositionAndParticleFadeOutTime =
						XMFLOAT4(GameConstants::EyeP1.x, GameConstants::EyeP1.y, GameConstants::EyeP1.z, GameConstants::Damage::DamageDuration);
				}
				else
				{
					glassShatterOnWindowSizeChange.LightPositionAndParticleFadeOutTime =
						XMFLOAT4(GameConstants::EyeP2.x, GameConstants::EyeP2.y, GameConstants::EyeP2.z, GameConstants::Damage::DamageDuration);
				}
				glassShatterOnWindowSizeChange.SpecularColorAndSpecularPower = XMFLOAT4(0.1f, 0.6f, 1.0f, GameConstants::Particles::SpecularPower);

				m_d3dContext->UpdateSubresource(
					m_glassShatterOnWindowSizeChangeBuffer.Get(), 0, nullptr, &glassShatterOnWindowSizeChange, 0, 0
					);
			}

			// UPDATE THE RENDERING CAMERA PER FRAME
			XMFLOAT4X4 viewMatrix;

			XMStoreFloat4x4(
				&viewMatrix,
				XMMatrixTranspose(m_game->GameCamera()->View())
				);

			LightPrePassPerFrame lightPrePassPerFrame = {};
			lightPrePassPerFrame.View = viewMatrix;

			m_d3dContext->UpdateSubresource(
				m_lightPrePassPerFrameBuffer.Get(),
				0,
				nullptr,
				&lightPrePassPerFrame,
				0,
				0
				);

			LightFinalPassPerFrame lightFinalPassPerFrame = {};
			lightFinalPassPerFrame.View = viewMatrix;
			lightFinalPassPerFrame.AmbientColor = GameConstants::AmbientColor;
			m_d3dContext->UpdateSubresource(
				m_lightFinalPassPerFrameBuffer.Get(),
				0,
				nullptr,
				&lightFinalPassPerFrame,
				0,
				0
				);

			LightPassViewParameters lightPassViewParameters = {};
			auto viewPos = m_game->GameCamera()->Eye();
			lightPassViewParameters.ViewPosition = XMFLOAT4(viewPos.x, viewPos.y, viewPos.z, 0.0f);

			m_d3dContext->UpdateSubresource(
				m_lightPassViewParametersBuffer.Get(),
				0,
				nullptr,
				&lightPassViewParameters,
				0,
				0
				);
			{
				auto damageTime = m_game->DamageTimer();
				auto currentTime = m_game->GetGameTimer()->PlayingTime();

				if (damageTime >= currentTime)
				{
					GlassShatterPerFrame glassShatterPerFrame = {};
					glassShatterPerFrame.EyePosition = lightPassViewParameters.ViewPosition;
					glassShatterPerFrame.RotAmountTranslatAmountTimeSinceStart = XMFLOAT4(XM_PI * 3.0f,
						-GameConstants::Particles::TranslationAmount,
						damageTime - currentTime,
						0.0f);
					glassShatterPerFrame.View = viewMatrix;

					m_d3dContext->UpdateSubresource(
						m_glassShatterPerFrameBuffer.Get(), 0, nullptr, &glassShatterPerFrame, 0, 0
						);
				}
			}
			// LIGHT PRE-PASS
			{
				m_d3dContext->IASetInputLayout(m_vertexLayout.Get());

				m_d3dContext->VSSetConstantBuffers(0, 1, m_lightPrePassOnWindowSizeChangeBuffer.GetAddressOf());
				m_d3dContext->VSSetConstantBuffers(1, 1, m_lightPrePassPerFrameBuffer.GetAddressOf());
				m_d3dContext->VSSetConstantBuffers(2, 1, m_lightPrePassPerMeshBuffer.GetAddressOf());

				m_d3dContext->PSSetConstantBuffers(0, 1, m_lightPrePassOnWindowSizeChangeBuffer.GetAddressOf());
				m_d3dContext->PSSetConstantBuffers(1, 1, m_lightPrePassPerFrameBuffer.GetAddressOf());
				m_d3dContext->PSSetConstantBuffers(2, 1, m_lightPrePassPerMeshBuffer.GetAddressOf());

				m_d3dContext->PSSetSamplers(0, 1, m_samplerLinear.GetAddressOf());

				m_d3dContext->OMSetBlendState(m_blendStateOpaque.Get(), nullptr, 0xffffffff);
				m_d3dContext->OMSetDepthStencilState(m_depthStencilState.Get(), 1);
				m_d3dContext->RSSetState(m_rasterizerState.Get());

				// LOOP THROUGH ALL GAME OBJECTS AND RENDER

				auto objects = m_game->RenderObjects();
				bool drawingNormalMappedObject = false;
				for (GameObject^ object : objects)
				{
					if(!object->Active())
						continue;

					if (MjpegCamera^ cam = dynamic_cast<MjpegCamera^>(object))
					{
						cam->Render(m_d3dContext.Get(), m_lightPrePassPerMeshBuffer.Get(), true, i);
					}
					else
					{
						if (auto material = dynamic_cast<NormalMappedMaterial^>((object)->GetMaterial()))
						{
							(object)->Render(m_d3dContext.Get(), m_lightPrePassPerMeshBuffer.Get(), true);
						}
						else
						{
							(object)->Render(m_d3dContext.Get(), m_lightPrePassPerMeshBuffer.Get(), true);
						}
					}
				}

				m_d3dContext->OMSetBlendState(m_blendStateAlpha.Get(), nullptr, 0xffffffff);
				objects = m_game->TransparentRenderObjects();
				for (GameObject^ object : objects)
				{
					if(!object->Active())
						continue;

					(object)->Render(m_d3dContext.Get(), m_lightPrePassPerMeshBuffer.Get(), true);
				}
			}

			// LIGHT PASS
			{
				const float lightPassClearColor[] = { 0.0f, 0.0f, 0.0f, 0.0f };
				m_d3dContext->OMSetRenderTargets(1, m_lightPassLightBufferRenderTarget->GetRenderTargetView(), nullptr);
				m_d3dContext->ClearRenderTargetView(*m_lightPassLightBufferRenderTarget->GetRenderTargetView(), lightPassClearColor);
				m_d3dContext->OMSetBlendState(m_blendStateAdditive.Get(), nullptr, 0xffffffff);

				LightPassOnWindowSizeChange lightPassOnWindowSizeChange = {};
				lightPassOnWindowSizeChange.TransformationMatrix = GenerateQuadMatrix(m_renderTargetSize.Width, m_renderTargetSize.Height);
				m_d3dContext->UpdateSubresource(m_lightPassOnWindowSizeChangeBuffer.Get(), 0, nullptr, &lightPassOnWindowSizeChange, 0, 0);

				m_d3dContext->VSSetShader(m_lightPassVertexShader.Get(), nullptr, 0);
				m_d3dContext->VSSetConstantBuffers(0, 1, m_lightPassOnWindowSizeChangeBuffer.GetAddressOf());

				m_d3dContext->PSSetShaderResources(0, 1, m_lightPrePassPositionBufferRenderTarget->GetShaderResourceView());
				m_d3dContext->PSSetShaderResources(1, 1, m_lightPrePassNormalBufferRenderTarget->GetShaderResourceView());
				m_d3dContext->PSSetSamplers(0, 1, m_samplerPoint.GetAddressOf());

				m_d3dContext->PSSetConstantBuffers(0, 1, m_lightPassOnWindowSizeChangeBuffer.GetAddressOf());
				m_d3dContext->PSSetConstantBuffers(2, 1, m_lightPassViewParametersBuffer.GetAddressOf());

				const UINT vertexStride = sizeof(PTVertex);
				const UINT vertexOffset = 0;

				m_d3dContext->IASetVertexBuffers(0, 1, m_drawTexturedQuadVertexBuffer.GetAddressOf(), &vertexStride, &vertexOffset);
				m_d3dContext->IASetInputLayout(m_drawTexturedQuadVertexLayout.Get());
				m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

				m_d3dContext->IASetIndexBuffer(m_drawTexturedQuadIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);

				UpdateLights();

				for (auto light : m_allLights)
				{
					light->Render(m_d3dContext.Get());
				}

				const static std::unique_ptr<ID3D11ShaderResourceView*> nullSRV(new ID3D11ShaderResourceView*(nullptr));

				m_d3dContext->PSSetShaderResources(0, 1, nullSRV.get());
				m_d3dContext->PSSetShaderResources(1, 1, nullSRV.get());
			}

			//DebugDrawTarget(m_lightPassLightBufferRenderTarget->GetShaderResourceView());
			//return;

			// POST LIGHT PASS COMBINE
			{

				// If applying bloom, we need to render the scene to a render target so we can then apply the bloom effects, otherwise just render to the back buffer(s).
				if (m_applyBloom)
				{
					m_d3dContext->OMSetRenderTargets(1, m_mainRenderTarget->GetRenderTargetView(), m_mainRenderTarget->GetDepthStencilView());
					m_d3dContext->ClearRenderTargetView(*m_mainRenderTarget->GetRenderTargetView(), ClearColor);
					m_d3dContext->ClearDepthStencilView(m_mainRenderTarget->GetDepthStencilView(), D3D11_CLEAR_DEPTH, 1.0f, 0);
				}
				else
				{
					// Iterate through the number of rendering passes to be completed.
					if (m_stereoEnabled && i > 0)
					{
						// Doing the Right Eye View.
						m_d3dContext->OMSetRenderTargets(1, m_d3dRenderTargetViewRight.GetAddressOf(), m_d3dDepthStencilView.Get());
						m_d3dContext->ClearRenderTargetView(m_d3dRenderTargetViewRight.Get(), ClearColor);
						m_d3dContext->ClearDepthStencilView(m_d3dDepthStencilView.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
						m_d2dContext->SetTarget(m_d2dTargetBitmapRight.Get());
					}
					else
					{
						// Doing the Mono or Left Eye View.
						m_d3dContext->OMSetRenderTargets(1, m_d3dRenderTargetView.GetAddressOf(), m_d3dDepthStencilView.Get());
						m_d3dContext->ClearRenderTargetView(m_d3dRenderTargetView.Get(), ClearColor);
						m_d3dContext->ClearDepthStencilView(m_d3dDepthStencilView.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
						m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
					}
				}

				m_d3dContext->IASetInputLayout(m_vertexLayout.Get());

				m_d3dContext->VSSetConstantBuffers(0, 1, m_lightFinalPassOnWindowSizeChangeBuffer.GetAddressOf());
				m_d3dContext->VSSetConstantBuffers(1, 1, m_lightFinalPassPerFrameBuffer.GetAddressOf());
				m_d3dContext->VSSetConstantBuffers(2, 1, m_lightFinalPassPerMeshBuffer.GetAddressOf());

				m_d3dContext->PSSetConstantBuffers(0, 1, m_lightFinalPassOnWindowSizeChangeBuffer.GetAddressOf());
				m_d3dContext->PSSetConstantBuffers(1, 1, m_lightFinalPassPerFrameBuffer.GetAddressOf());
				m_d3dContext->PSSetConstantBuffers(2, 1, m_lightFinalPassPerMeshBuffer.GetAddressOf());

				m_d3dContext->PSSetSamplers(0, 1, m_samplerLinear.GetAddressOf());
				m_d3dContext->PSSetSamplers(1, 1, m_samplerPoint.GetAddressOf());

				m_d3dContext->PSSetShaderResources(0, 1, m_lightPassLightBufferRenderTarget->GetShaderResourceView());

				m_d3dContext->OMSetBlendState(m_blendStateOpaque.Get(), nullptr, 0xffffffff);
				m_d3dContext->OMSetDepthStencilState(m_depthStencilState.Get(), 1);
				m_d3dContext->RSSetState(m_rasterizerState.Get());

				m_d3dContext->VSSetShader(m_lightFinalPassVertexShader.Get(), nullptr, 0);
				m_d3dContext->PSSetShader(m_lightFinalPassPixelShader.Get(), nullptr, 0);

				// LOOP THROUGH ALL GAME OBJECTS AND RENDER

				auto objects = m_game->RenderObjects();
				for (GameObject^ object : objects)
				{
					if(!object->Active())
						continue;

					if (MjpegCamera^ cam = dynamic_cast<MjpegCamera^>(object))
					{
						cam->Render(m_d3dContext.Get(), m_lightFinalPassPerMeshBuffer.Get(), false, i);
					}
					else
					{
						if (auto material = dynamic_cast<NormalMappedMaterial^>((object)->GetMaterial()))
						{
							(object)->Render(m_d3dContext.Get(), m_lightFinalPassPerMeshBuffer.Get(), false);
						}
						else
						{
							(object)->Render(m_d3dContext.Get(), m_lightFinalPassPerMeshBuffer.Get(), false);
						}
					}
				}

				m_d3dContext->OMSetBlendState(m_blendStateAlpha.Get(), nullptr, 0xffffffff);
				objects = m_game->TransparentRenderObjects();
				for (GameObject^ object : objects)
				{
					if(!object->Active())
						continue;

					(object)->Render(m_d3dContext.Get(), m_lightFinalPassPerMeshBuffer.Get(), false);
				}
			}

			// APPLY BLOOM
			if (m_applyBloom)
			{
				m_d3dContext->OMSetBlendState(m_blendStateOpaque.Get(), nullptr, 0xffffffff);

				float bloomTargetWidth = m_renderTargetSize.Width;
				float bloomTargetHeight = m_renderTargetSize.Height;

				// EXTRACT BRIGHTNESS
				{
					auto bloomExtractConstantBuffer = BloomExtractConstantBufferChangesEveryFrame();
					bloomExtractConstantBuffer.BloomThreshold = m_currentBloomThreshold;
					m_d3dContext->UpdateSubresource(m_bloomExtractConstantBufferChangesEveryFrame.Get(), 0, nullptr, &bloomExtractConstantBuffer, 0, 0);

					m_d3dContext->OMSetRenderTargets(1, m_bloomRenderTargetOne->GetRenderTargetView(), nullptr);
					m_d3dContext->PSSetConstantBuffers(0, 1, m_bloomExtractConstantBufferChangesEveryFrame.GetAddressOf());
					RenderTexturedQuad(bloomTargetWidth, bloomTargetHeight, m_mainRenderTarget->GetShaderResourceView(), nullptr, m_bloomExtractPixelShader.Get());
				}

				// BLUR HORIZONTALLY
				{
					float gaussian;
					float offset = 0.0f;
					float deltaX;
					float deltaY;

					deltaX = 1.0f / m_renderTargetSize.Width;
					deltaY = 0.0f;

					// theta is the blur amount.
					float theta = GameConstants::BloomValues::BlurAmount;

					gaussian = static_cast<float>((1.0 / sqrtf(2.0f * XM_PI * theta)) *
						expf(-(offset * offset) / (2 * theta * theta)));

					auto bloomBlurPerFrame = BloomBlurConstantBufferChangesEveryFrame();
					bloomBlurPerFrame.sampleOffsetsAndWeights[0].x = 0.0f;
					bloomBlurPerFrame.sampleOffsetsAndWeights[0].y = 0.0f;
					bloomBlurPerFrame.sampleOffsetsAndWeights[0].z = gaussian;

					int sampleCount = BLUR_SAMPLE_COUNT;

					// Running total of weights.
					float totalWeights = gaussian;

					// Add pairs of additional sample taps, positioned
					// along a line in both directions from the center.
					for (int i = 0; i < sampleCount / 2; i++)
					{
						// Store weights for the positive and negative taps.
						offset = static_cast<float>(i + 1);

						gaussian = static_cast<float>((1.0 / sqrtf(2.0f * XM_PI * theta)) *
							expf(-(offset * offset) / (2 * theta * theta)));

						float weight = gaussian;

						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 1].z = weight;
						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 2].z = weight;

						totalWeights += weight * 2;

						// To get the maximum amount of blurring from a limited number of
						// pixel shader samples, we take advantage of the bilinear filtering
						// hardware inside the texture fetch unit. If we position our texture
						// coordinates exactly halfway between two texels, the filtering unit
						// will average them for us, giving two samples for the price of one.
						// This allows us to step in units of two texels per sample, rather
						// than just one at a time. The 1.5 offset kicks things off by
						// positioning us nicely in between two texels.
						float sampleOffset = i * 2 + 1.5f;

						XMFLOAT2 delta(deltaX * sampleOffset, deltaY * sampleOffset);

						// Store texture coordinate offsets for the positive and negative taps.
						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 1].x = delta.x;
						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 1].y = delta.y;
						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 2].x = -delta.x;
						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 2].y = -delta.y;
					}

					// Normalize the list of sample weightings, so they will always sum to one.
					for (int i = 0; i < BLUR_SAMPLE_COUNT; i++)
					{
						bloomBlurPerFrame.sampleOffsetsAndWeights[i].z /= totalWeights;
					}

					m_d3dContext->UpdateSubresource(m_bloomBlurHorizontalConstantBufferChangesEveryFrame.Get(), 0, nullptr, &bloomBlurPerFrame, 0, 0);

					m_d3dContext->OMSetRenderTargets(1, m_bloomRenderTargetTwo->GetRenderTargetView(), nullptr);
					m_d3dContext->PSSetConstantBuffers(0, 1, m_bloomBlurHorizontalConstantBufferChangesEveryFrame.GetAddressOf());
					RenderTexturedQuad(bloomTargetWidth, bloomTargetHeight, m_bloomRenderTargetOne->GetShaderResourceView(), nullptr, m_bloomBlurPixelShader.Get());
				}

				// BLUR VERTICALLY
				{
					float gaussian;
					float offset = 0.0f;
					float deltaX;
					float deltaY;

					deltaX = 0.0f;
					deltaY = 1.0f / m_renderTargetSize.Height;

					// theta is the blur amount.
					float theta = GameConstants::BloomValues::BlurAmount;

					gaussian = static_cast<float>((1.0 / sqrtf(2.0f * XM_PI * theta)) *
						expf(-(offset * offset) / (2 * theta * theta)));

					auto bloomBlurPerFrame = BloomBlurConstantBufferChangesEveryFrame();
					bloomBlurPerFrame.sampleOffsetsAndWeights[0].x = 0.0f;
					bloomBlurPerFrame.sampleOffsetsAndWeights[0].y = 0.0f;
					bloomBlurPerFrame.sampleOffsetsAndWeights[0].z = gaussian;
					int sampleCount = BLUR_SAMPLE_COUNT;

					// Running total of weights.
					float totalWeights = gaussian;//bloomBlurPerFrame->sampleOffsetsAndWeights[0].z;

					// Add pairs of additional sample taps, positioned
					// along a line in both directions from the center.
					for (int i = 0; i < sampleCount / 2; i++)
					{
						// Store weights for the positive and negative taps.
						offset = static_cast<float>(i + 1);

						gaussian = static_cast<float>((1.0 / sqrtf(2.0f * XM_PI * theta)) *
							expf(-(offset * offset) / (2 * theta * theta)));

						float weight = gaussian;

						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 1].z = weight;
						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 2].z = weight;

						totalWeights += weight * 2;

						// To get the maximum amount of blurring from a limited number of
						// pixel shader samples, we take advantage of the bilinear filtering
						// hardware inside the texture fetch unit. If we position our texture
						// coordinates exactly halfway between two texels, the filtering unit
						// will average them for us, giving two samples for the price of one.
						// This allows us to step in units of two texels per sample, rather
						// than just one at a time. The 1.5 offset kicks things off by
						// positioning us nicely in between two texels.
						float sampleOffset = i * 2 + 1.5f;

						XMFLOAT2 delta(deltaX * sampleOffset, deltaY * sampleOffset);

						// Store texture coordinate offsets for the positive and negative taps.
						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 1].x = delta.x;
						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 1].y = delta.y;
						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 2].x = -delta.x;
						bloomBlurPerFrame.sampleOffsetsAndWeights[i * 2 + 2].y = -delta.y;
					}

					// Normalize the list of sample weightings, so they will always sum to one.
					for (int i = 0; i < BLUR_SAMPLE_COUNT; i++)
					{
						bloomBlurPerFrame.sampleOffsetsAndWeights[i].z /= totalWeights;
					}

					m_d3dContext->UpdateSubresource(m_bloomBlurVerticalConstantBufferChangesEveryFrame.Get(), 0, nullptr, &bloomBlurPerFrame, 0, 0);

					m_d3dContext->OMSetRenderTargets(1, m_bloomRenderTargetOne->GetRenderTargetView(), nullptr);
					m_d3dContext->PSSetConstantBuffers(0, 1, m_bloomBlurVerticalConstantBufferChangesEveryFrame.GetAddressOf());
					RenderTexturedQuad(bloomTargetWidth, bloomTargetHeight, m_bloomRenderTargetTwo->GetShaderResourceView(), nullptr, m_bloomBlurPixelShader.Get());
				}

				// The final stage of bloom is to combine the bloom effect results with the original scene and draw it onto the back buffer(s).
				// Here we do that, while also allowing for a simple pass-through in case we want to disable bloom for any reason. 

				// Iterate through the number of rendering passes to be completed.
				if (m_stereoEnabled && i > 0)
				{
					// Doing the Right Eye View.	                
					m_d3dContext->OMSetRenderTargets(1, m_d3dRenderTargetViewRight.GetAddressOf(), nullptr);
					m_d3dContext->ClearRenderTargetView(m_d3dRenderTargetViewRight.Get(), ClearColor);
					m_d2dContext->SetTarget(m_d2dTargetBitmapRight.Get());
				}
				else
				{
					// Doing the Mono or Left Eye View.
					m_d3dContext->OMSetRenderTargets(1, m_d3dRenderTargetView.GetAddressOf(), nullptr);
					m_d3dContext->ClearRenderTargetView(m_d3dRenderTargetView.Get(), ClearColor);
					m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
				}

				// COMBINE

				auto bloomCombinePerFrame = BloomCombineConstantBufferChangesEveryFrame();

				bloomCombinePerFrame.values.x = GameConstants::BloomValues::BloomIntensity;
				bloomCombinePerFrame.values.y = GameConstants::BloomValues::BaseIntensity;
				bloomCombinePerFrame.values.z = GameConstants::BloomValues::BloomSaturation;
				bloomCombinePerFrame.values.w = GameConstants::BloomValues::BaseSaturation;

				m_d3dContext->UpdateSubresource(m_bloomCombineConstantBufferChangesEveryFrame.Get(), 0, nullptr, &bloomCombinePerFrame, 0, 0);

				m_d3dContext->PSSetConstantBuffers(0, 1, m_bloomCombineConstantBufferChangesEveryFrame.GetAddressOf());
				RenderTexturedQuad(m_renderTargetSize.Width, m_renderTargetSize.Height, m_bloomRenderTargetOne->GetShaderResourceView(), m_mainRenderTarget->GetShaderResourceView(), m_bloomCombinePixelShader.Get());

			}

			auto damageTimer = m_game->DamageTimer();
			auto currentTime = m_game->GetGameTimer()->PlayingTime();

			// DRAW DAMAGE PARTICLES
			if (damageTimer >= currentTime && GameConstants::Particles::Enabled)
			{
				GlassShatterPerMesh cb = {};
				XMStoreFloat4x4(&cb.World, XMMatrixTranspose(XMMatrixMultiply(
					m_game->GameCamera()->World(),
					XMMatrixTranslation(
					m_game->IsPlayerOne() ? -9.5f : 9.5f,
					-5.0f,
					m_game->IsPlayerOne() ? (GameConstants::Particles::StartDepthOffset) : (-GameConstants::Particles::StartDepthOffset)
					)
					)));
				m_d3dContext->UpdateSubresource(m_glassShatterPerMeshBuffer.Get(), 0, nullptr, &cb, 0, 0);
				m_d3dContext->RSSetState(m_rasterizerStateCullModeNone.Get());
				m_d3dContext->OMSetBlendState(m_blendStateAlpha.Get(), nullptr, 0xffffffff);

				static ID3D11Buffer* glassConstantBuffers[3] = {};
				glassConstantBuffers[0] = m_glassShatterOnWindowSizeChangeBuffer.Get();
				glassConstantBuffers[1] = m_glassShatterPerFrameBuffer.Get();
				glassConstantBuffers[2] = m_glassShatterPerMeshBuffer.Get();

				m_d3dContext->VSSetShader(m_glassShatterVertexShader.Get(), nullptr, 0);
				m_d3dContext->VSSetConstantBuffers(0, 3, glassConstantBuffers);

				m_d3dContext->GSSetShader(m_glassShatterGeometryShader.Get(), nullptr, 0);
				m_d3dContext->GSSetConstantBuffers(0, 3, glassConstantBuffers);

				m_d3dContext->PSSetShader(m_glassShatterPixelShader.Get(), nullptr, 0);
				m_d3dContext->PSSetConstantBuffers(0, 3, glassConstantBuffers);
				m_d3dContext->PSSetSamplers(0, 1, m_samplerLinear.GetAddressOf());
				m_d3dContext->PSSetShaderResources(0, 1, m_GlassTexture.GetAddressOf());

				m_glassShatterScreenMesh->Render(m_d3dContext.Get(), true);

				m_d3dContext->RSSetState(m_rasterizerState.Get());
				m_d3dContext->GSSetShader(nullptr, nullptr, 0);
			}

			// DRAW DAMAGE EFFECT
			if (damageTimer >= currentTime)
			{
				if (!GameConstants::Particles::Enabled)
				{
					m_d3dContext->OMSetBlendState(m_blendStateAlpha.Get(), nullptr, 0xffffffff);
				}
				// Use a power curve for greater effect: http://blogs.msdn.com/b/shawnhar/archive/2007/03/30/massaging-thumbsticks.aspx .
				auto fade = powf((damageTimer - currentTime) / GameConstants::Damage::DamageDuration, 0.5f);
				RenderTexturedQuad(m_renderTargetSize.Width, m_renderTargetSize.Height, m_DamageTexture.GetAddressOf(), nullptr, nullptr, nullptr, XMFLOAT4(0.7f * fade, 0.0f, 0.0f, 0.7f * fade));
			}

		}
		else
		{
			// WE AREN'T RENDERING THE GAME YET SO SIMPLY CLEAR THE SCREEN TO BLACK
			const float ClearColor[4] = {0.0f, 0.0f, 0.0f, 1.0f};

			if (m_stereoEnabled && i > 0)
			{
				// Doing the Right Eye View.	                
				m_d3dContext->OMSetRenderTargets(1, m_d3dRenderTargetViewRight.GetAddressOf(), nullptr);
				m_d3dContext->ClearRenderTargetView(m_d3dRenderTargetViewRight.Get(), ClearColor);
			}
			else
			{
				// Doing the Mono or Left Eye View.
				m_d3dContext->OMSetRenderTargets(1, m_d3dRenderTargetView.GetAddressOf(), nullptr);
				m_d3dContext->ClearRenderTargetView(m_d3dRenderTargetView.Get(), ClearColor);
			}
		}
	}
	Present();
}

void GameRenderer::UpdateLights()
{
	auto pucks = m_game->GetPucks();

	m_pointLights[0]->LightPosition(10.0f, 10.0f, 50.0f);
	m_pointLights[1]->LightPosition(10.0f, 10.0f, 50.0f);
	m_pointLights[2]->LightPosition(10.0f, 10.0f, 50.0f);

	m_pointLights[3]->LightPosition( m_game->Paddle2()->Position().x,   m_game->Paddle2()->Position().y, m_game->Paddle2()->Position().z); //  GameConstants::MaxBound.z + 2.0f );
	m_pointLights[4]->LightPosition( m_game->Paddle1()->Position().x,   m_game->Paddle1()->Position().y, m_game->Paddle1()->Position().z); // GameConstants::MinBound.z - 2.0f );

	if (pucks.size() > 0)
	{
		if(pucks[0]->Active())
		{
			const auto& pos = pucks[0]->Position();
			m_pointLights[0]->LightPosition(pos.x, pos.y, pos.z);
		}
	}
	if (pucks.size() > 1)
	{
		if(pucks[1]->Active())
		{
			const auto& pos = pucks[1]->Position();
			m_pointLights[1]->LightPosition(pos.x, pos.y, pos.z);
		}
	}
	if (pucks.size() > 2)
	{
		if(pucks[2]->Active())
		{
			const auto& pos = pucks[2]->Position();
			m_pointLights[2]->LightPosition(pos.x, pos.y, pos.z);
		}
	}
}

XMFLOAT4X4 GameRenderer::GenerateQuadMatrix(float destWidth, float destHeight)
{
	// Compute the matrix.
	float xScale = (destWidth  > 0) ? 1.0f / destWidth  : 0.0f;
	float yScale = (destHeight > 0) ? -1.0f / destHeight : 0.0f;

	return XMFLOAT4X4
		(
		xScale * 2.0f,  0.0f,           0.0f,       0.0f,
		0.0f,           yScale * 2.0f,  0.0f,       0.0f,
		0.0f,           0.0f,           1.0f,       0.0f,
		-1.0f - xScale, 1.0f - yScale,  0.0f,       1.0f
		);
}

void GameRenderer::RenderTexturedQuad(float destWidth, float destHeight, ID3D11ShaderResourceView* const* texture1, ID3D11ShaderResourceView* const* texture2, ID3D11PixelShader* pixelShader, ID3D11SamplerState* samplerState, XMFLOAT4 tintColor)
{
	const UINT vertexStride = sizeof(PTVertex);
	const UINT vertexOffset = 0;

	if (pixelShader == nullptr)
	{
		pixelShader = m_drawTexturedQuadPixelShader.Get();
		m_d3dContext->PSSetConstantBuffers(0, 1, m_drawTexturedQuadConstantBufferChangesEveryDraw.GetAddressOf());
	}

	if (samplerState == nullptr)
	{
		samplerState = m_samplerLinear.Get();
	}

	m_d3dContext->IASetInputLayout(m_drawTexturedQuadVertexLayout.Get());
	m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	m_d3dContext->IASetVertexBuffers(0, 1, m_drawTexturedQuadVertexBuffer.GetAddressOf(), &vertexStride, &vertexOffset);
	m_d3dContext->IASetIndexBuffer(m_drawTexturedQuadIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);

	// Next need to map and create the matrix for the draw textures quad constant buffer.
	if (m_drawTexturedQuadConstantBufferChangesEveryDraw != nullptr)
	{
		DrawTexturedQuadConstantBufferChangeOnResize cb = {};
		cb.transformationMatrix = GenerateQuadMatrix(destWidth, destHeight);
		cb.tintColor = tintColor;

		m_d3dContext->UpdateSubresource(m_drawTexturedQuadConstantBufferChangesEveryDraw.Get(), 0, nullptr, &cb, 0, 0);
	}

	m_d3dContext->OMSetDepthStencilState(m_depthStencilStateNone.Get(), 0);
	m_d3dContext->VSSetShader(m_drawTexturedQuadVertexShader.Get(), nullptr, 0);
	m_d3dContext->VSSetConstantBuffers(0, 1, m_drawTexturedQuadConstantBufferChangesEveryDraw.GetAddressOf());
	m_d3dContext->PSSetShader(pixelShader, nullptr, 0);
	m_d3dContext->PSSetSamplers(0, 1, &samplerState);
	m_d3dContext->PSSetShaderResources(0, 1, texture1);
	if (texture2 != nullptr)
	{
		m_d3dContext->PSSetShaderResources(1, 1, texture2);
	}
	m_d3dContext->DrawIndexed(6, 0, 0);

	const static std::unique_ptr<ID3D11ShaderResourceView*> nullSRV(new ID3D11ShaderResourceView*(nullptr));

	m_d3dContext->PSSetShaderResources(0, 1, nullSRV.get());

	if (texture2 != nullptr)
	{
		m_d3dContext->PSSetShaderResources(1, 1, nullSRV.get());
	}
}

void GameRenderer::DebugDrawTarget(ID3D11ShaderResourceView* const* target)
{
	const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
	m_d3dContext->OMSetRenderTargets(1, m_d3dRenderTargetView.GetAddressOf(), nullptr);
	m_d3dContext->ClearRenderTargetView(m_d3dRenderTargetView.Get(), clearColor);

	DrawTexturedQuadConstantBufferChangeOnResize cb = {};
	cb.tintColor = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
	cb.transformationMatrix = GenerateQuadMatrix(m_renderTargetSize.Width, m_renderTargetSize.Height);
	m_d3dContext->UpdateSubresource(m_drawTexturedQuadConstantBufferChangesEveryDraw.Get(), 0, nullptr, &cb, 0, 0);
	RenderTexturedQuad(m_renderTargetSize.Width, m_renderTargetSize.Height, target);
	Present();
}
